#from threading import Thread
from multiprocessing import Process, Queue
import math

class slicer():
    
    def __init__(self, triangles, minVertex, maxVertex):
        self.T = triangles
        self.min = minVertex
        self.max = maxVertex
        self.layers = []
        
    def setResolution(self,resolution):
        self.resolution = resolution
        self.layers=[[]]*(int((self.max+self.resolution-self.min)/self.resolution)+1)
        
    def getResolution(self):
        return self.resolution
    
    def getMax(self):
        return self.max
    
    def getMin(self):
        return self.min
   
    def makeSlice(self, n, p, height):
       
        if (self.T[n][0][2] == height and self.T[n][1][2] == height and self.T[n][2][2] == height):
            for i in range(4):
                for j in range(3):
                    p[i][j] = self.T[n][i][j]
            p[4] = 3
            return

        points = [self.T[n][0], self.T[n][1], self.T[n][2]]
        order = [2,1,0]

        if (points[order[0]][2] < height or points[order[2]][2] > height):
            p[4] = 0
            return
    
        #I can has math
        p[3] = self.T[n][3]
        p[4] = 2
        z1 = 0
        z2 = 2
		
        scale = (height-points[order[z1]][2])/(points[order[z2]][2]-points[order[z1]][2])
        p[0][0] = points[order[z1]][0]+scale*(points[order[z2]][0]-points[order[z1]][0])
        p[0][1] = points[order[z1]][1]+scale*(points[order[z2]][1]-points[order[z1]][1])
        p[0][2] = height
        p[1][2] = height
    
        if (points[order[1]][2] == points[order[0]][2]):
            z1 = 1
            z2 = 2
        elif points[order[1]][2] > height:
            z1 = 1
            z2 = 2
        else:
            z1 = 0
            z2 = 1

        scale = (height-points[order[z1]][2])/(points[order[z2]][2]-points[order[z1]][2])
        
        p[1][0] = points[order[z1]][0]+scale*(points[order[z2]][0]-points[order[z1]][0])
        p[1][1] = points[order[z1]][1]+scale*(points[order[z2]][1]-points[order[z1]][1])
		
    def makeSliceRound(self, n, p, height):
        height = round(height, 10)
        
        if (round(self.T[n][0][2], 10) == height and round(self.T[n][1][2], 10) == height and round(self.T[n][2][2], 10) == height):
            for i in range(4):
                for j in range(3):
                    p[i][j] = self.T[n][i][j]
            p[4] = 3
            return

        points = [self.T[n][0], self.T[n][1], self.T[n][2]]
        order = [2,1,0]

        if (round(points[order[0]][2], 10) < height or round(points[order[2]][2], 10) > height):
            p[4] = 0
            return
    
        #I can has math
        p[3] = self.T[n][3]
        p[4] = 2
        z1 = 0
        z2 = 2
		
        scale = (height-points[order[z1]][2])/(points[order[z2]][2]-points[order[z1]][2])
        p[0][0] = points[order[z1]][0]+scale*(points[order[z2]][0]-points[order[z1]][0])
        p[0][1] = points[order[z1]][1]+scale*(points[order[z2]][1]-points[order[z1]][1])
        p[0][2] = height
        p[1][2] = height
    
        if (round(points[order[1]][2], 10) == round(points[order[0]][2], 10)):
            z1 = 1
            z2 = 2
        elif round(points[order[1]][2], 10) > height:
            z1 = 1
            z2 = 2
        else:
            z1 = 0
            z2 = 1

        scale = (height-points[order[z1]][2])/(points[order[z2]][2]-points[order[z1]][2])
        
        p[1][0] = points[order[z1]][0]+scale*(points[order[z2]][0]-points[order[z1]][0])
        p[1][1] = points[order[z1]][1]+scale*(points[order[z2]][1]-points[order[z1]][1])
    
	#Currently not working, damn GIL forcing me to use multiprocesses rather than threads to exploit parallelism.
    def sliceAllThreaded(self, num):
        try:
            threads = []
            z = self.min
            height = self.max-z
            q = Queue()
            while z <= self.max:
                step = z+round((height/num),int(math.log10(self.resolution)*-1))
                if step > height:
                    step = height+self.resolution
#                t=Thread(target=self.getSliceThread, args=(z,step))
                t=Process(target=self.getSliceThread, args=(q,z,step))
                t.start()
                threads.append(t)
                z = step 
            print "num threads: ",len(threads)
            while True:
                try:
                    tmp = q.get_no_wait()
                    self.layers[tmp[0]] = tmp[1]
                except Queue.Empty:
                    break
            for t in threads:
                t.join()
            while True:
                try:
                    tmp = q.get_no_wait()
                    self.layers[tmp[0]] = tmp[1]
                except Queue.Empty:
                    break
        except:
            print "Why no can has thread??"
            self.sliceAll()
    
    def getSliceThread(self, q, z, limit):
        while z <= limit+self.resolution:
            tmp = []
            for i in range(len(self.T)):
                temp = [[0,0,0],[0,0,0],[0,0,0],[0,0,0],0]
                self.makeSlice(i,temp,z)
                if temp[4] != 0:
                    tmp.append(temp)
                    q.put(((int)(z/self.resolution), tmp))
                    #self.layers[(int)(z/self.resolution)]=tmp
            z += self.resolution
            q.close()
    
    def sliceAll(self):
        z = self.min
        while z <= (self.max+self.resolution):
            tmp = []
            for i in range(len(self.T)):
                temp = [[0,0,0],[0,0,0],[0,0,0],[0,0,0],0]
                self.makeSlice(i,temp,z)
                if temp[4] != 0:
                    tmp.append(temp)
            self.layers[(int)(z/self.resolution)]=tmp
            z += self.resolution
            
    def getSlice(self, z):
        if self.layers[(int)(z/self.resolution)]:
            tmp = self.layers[(int)(z/self.resolution)]
        else:
            tmp = []
            for i in range(len(self.T)):
                temp = [[0,0,0],[0,0,0],[0,0,0],[0,0,0],0]
                self.makeSlice(i,temp,z)
                if temp[4] != 0:
                    tmp.append(temp)
            self.layers[(int)(z/self.resolution)]=tmp
        return tmp
    
    def getLayers(self):
        return self.layers